package jwt

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"github.com/google/uuid"
)

// JWT is jwt middleware
func JWT() gin.HandlerFunc {
	return func(c *gin.Context) {
		authHeader := c.Request.Header.Get("Authorization")
		authHeader = strings.ReplaceAll(authHeader, "Bearer ", "")
		authHeader = strings.ReplaceAll(authHeader, "bearer ", "")

		if authHeader == "" {
			c.JSON(503, gin.H{
				"ResponseStatus": 503,
				"ResponseMsg":    "请求头中auth为空",
				"datas":          []string{},
			})
			c.Abort()
			return
		}
		mc, err := ParseToken(authHeader)
		if err != nil {
			c.JSON(504, gin.H{
				"ResponseStatus": 504,
				"ResponseMsg":    "无效的Token",
				"datas":          []string{},
			})
			c.Abort()
			return
		}

		c.Set("Authorization", authHeader)
		c.Set("institution_id", mc.InstitutionID)
		c.Set("user_id", mc.UserID)
		c.Next()
	}
}

var CustomSecret = []byte("ae1b0a366e5799ec906a6b7e316a62583aae68de837197168dc17a8352dc1d3f") // 小秘密
var HASH_SALT = "152a6ca23cd89789d1d987d0c4782416"                                            // 给汤加个底味

const TokenExpireDuration = time.Hour * 24 * 3

// const TokenExpireDuration = time.Second * 15

// CustomClaims 自定义声明类型 并内嵌jwt.RegisteredClaims
// jwt包自带的jwt.RegisteredClaims只包含了官方字段
// 假设我们这里需要额外记录一个username字段，所以要自定义结构体
// 如果想要保存更多信息，都可以添加到这个结构体中
type CustomClaims struct {
	// 可根据需要自行添加字段
	UserID               int    `json:"user_id"`
	InstitutionID        int    `json:"institution_id"`
	Aud                  string `json:"aud"`
	jwt.RegisteredClaims        // 内嵌标准的声明
}

// GenToken 生成JWT
func GenToken(uid, aid int) (string, string) {
	// 创建一个我们自己的声明
	uuid, err := uuid.NewRandom()
	if err != nil {
		fmt.Println("Error generating UUID:", err)
	}
	aud := "all"
	uuids := fmt.Sprintf("%v", uuid)
	uuids = strings.ReplaceAll(uuids, "-", "")
	claims := CustomClaims{
		uid, // 自定义字段
		aid, // 自定义字段
		aud,
		jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpireDuration)),
			NotBefore: jwt.NewNumericDate(time.Now()),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Issuer:    "https://zyterp.com", // 签发人
			Subject:   "zyt",                //主题
			ID:        uuids,
		},
	}
	// 使用指定的签名方法创建签名对象
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	accessToken, _ := token.SignedString(CustomSecret)

	// 将id和salt和时间戳连接起来
	toHash := uuids + HASH_SALT + strconv.Itoa(int(time.Now().Unix()))
	// 计算toHash的SHA256哈希
	h := sha256.New()
	h.Write([]byte(toHash))
	hashed := h.Sum(nil)
	// 将哈希转换为十六进制字符串
	refreshToken := hex.EncodeToString(hashed)
	// 使用指定的secret签名并获得完整的编码后的字符串token
	return accessToken, refreshToken
}

// ParseToken 解析JWT
func ParseToken(tokenString string) (*CustomClaims, error) {
	// 解析token
	// 如果是自定义Claim结构体则需要使用 ParseWithClaims 方法
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (i interface{}, err error) {
		// 直接使用标准的Claim则可以直接使用Parse方法
		//token, err := jwt.Parse(tokenString, func(token *jwt.Token) (i interface{}, err error) {
		return CustomSecret, nil
	})
	if err != nil {
		return nil, err
	}
	// 对token对象中的Claim进行类型断言
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { // 校验token
		return claims, nil
	}
	return nil, errors.New("invalid token")
}

type JwtMsg struct {
	UID int    `json:"uid"`
	Aid int    `json:"aid"`
	Aud string `json:"aud"`
	Iss string `json:"iss"`
	Sub string `json:"sub"`
	Exp int    `json:"exp"`
	Nbf int    `json:"nbf"`
	Iat int    `json:"iat"`
	Jti string `json:"jti"`
}

// ParseToken 解析JWT是否过期
func Expired(tokenString string) (bool, *JwtMsg, error) {
	// 解析token
	// 如果是自定义Claim结构体则需要使用 ParseWithClaims 方法
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (i interface{}, err error) {
		// 直接使用标准的Claim则可以直接使用Parse方法
		//token, err := jwt.Parse(tokenString, func(token *jwt.Token) (i interface{}, err error) {
		return CustomSecret, nil
	})
	if err == nil {
		if claims, ok := token.Claims.(*CustomClaims); ok { // 校验token
			// 对token对象中的Claim进行类型断言
			s, _ := json.Marshal(claims)
			var claims_json *JwtMsg
			json.Unmarshal(s, &claims_json)
			if claims_json.Exp > int(time.Now().Unix()) {
				return false, nil, errors.New("token 未过期")
			} else {
				return true, claims_json, nil
			}
		}
	}
	// 对token对象中的Claim进行类型断言

	return false, nil, errors.New("invalid token")
}
